"""
角色服务层
负责角色管理的业务逻辑
"""
from typing import List, Optional, Dict, Any
from datetime import datetime
from motor.motor_asyncio import AsyncIOMotorDatabase  # pyright: ignore[reportMissingImports]
from bson import ObjectId  # pyright: ignore[reportMissingImports]
from app.schemas.role import RoleCreate, RoleUpdate, RoleResponse
from app.models.user import UserRole
from app.services.permission_service import ROLE_PERMISSIONS
from app.enums.role import SystemRole, SYSTEM_ROLE_CONFIG, get_system_role_config
from app.models.role import RoleScope


def get_default_roles() -> List[Dict[str, Any]]:
    """
    获取默认角色列表
    从 ROLE_PERMISSIONS 自动生成权限列表，确保权限定义一致
    使用 SystemRole 枚举作为数据源
    """
    default_roles = []
    
    # 映射 SystemRole 到 UserRole（用于从 ROLE_PERMISSIONS 获取权限）
    role_mapping = {
        SystemRole.ADMIN: UserRole.ADMIN,
        SystemRole.USER: UserRole.USER,
        SystemRole.PROJECT_MANAGER: UserRole.PROJECT_MANAGER,
        SystemRole.PRODUCT_MANAGER: UserRole.PRODUCT_MANAGER,
        SystemRole.OPERATIONS_SPECIALIST: UserRole.OPERATIONS_SPECIALIST,
        SystemRole.OPS_ENGINEER: UserRole.OPS_ENGINEER,
        SystemRole.DEVELOPER: UserRole.DEVELOPER,
        SystemRole.TESTER: UserRole.TESTER,
        SystemRole.VIEWER: UserRole.VIEWER,
    }
    
    for system_role, role_config in SYSTEM_ROLE_CONFIG.items():
        # 获取对应的 UserRole 枚举（用于权限映射）
        user_role = role_mapping.get(system_role)
        
        role_data = {
            "name": role_config["name"],
            "code": role_config["code"],
            "scope": role_config.get("scope", RoleScope.PROJECT),
            "description": role_config["description"],
            "permissions": ROLE_PERMISSIONS.get(user_role, []) if user_role else []
        }
        default_roles.append(role_data)
    
    def _permission_weight(role: Dict[str, Any]) -> int:
        permissions = role.get("permissions", [])
        if "*" in permissions:
            return 10_000  # 最高权限
        return len(permissions)
    
    default_roles.sort(key=_permission_weight, reverse=True)
    
    return default_roles


# 保持向后兼容：导出 DEFAULT_ROLES
DEFAULT_ROLES = get_default_roles()


class RoleService:
    """角色服务类"""
    
    @staticmethod
    async def get_role_options(db: AsyncIOMotorDatabase) -> List[Dict[str, str]]:
        """获取角色选项列表（用于注册页面）"""
        # 查询激活的角色，排除管理员角色
        query = {"is_active": True, "code": {"$ne": "admin"}}
        cursor = db.roles.find(query).sort("created_at", -1)
        roles = await cursor.to_list(length=None)
        
        # 如果数据库中没有角色，返回默认角色选项（排除管理员）
        if not roles:
            role_options = []
            for role in DEFAULT_ROLES:
                if role["code"] != "admin":  # 排除管理员角色
                    role_options.append({
                        "label": role["name"],
                        "value": role["code"],
                        "description": role["description"]
                    })
            return role_options
        
        # 转换为选项格式
        role_options = []
        for role in roles:
            role_options.append({
                "label": role["name"],
                "value": role["code"],
                "description": role.get("description", "")
            })
        
        return role_options
    
    @staticmethod
    async def get_all_roles(db: AsyncIOMotorDatabase) -> List[Dict[str, Any]]:
        """获取所有角色（不分页）"""
        # 查询所有角色
        cursor = db.roles.find({}).sort("created_at", -1)
        roles = await cursor.to_list(length=None)
        
        # 如果数据库中没有角色，初始化默认角色
        if not roles:
            default_roles = await RoleService._init_default_roles(db)
            return default_roles
        
        # 转换数据格式
        role_list = []
        for role in roles:
            role["id"] = str(role["_id"])
            del role["_id"]
            role_list.append(role)
        
        return role_list
    
    @staticmethod
    async def get_roles(
        db: AsyncIOMotorDatabase, 
        status: Optional[str] = None,
        is_system: Optional[bool] = None,
        scope: Optional[str] = None
    ) -> List[Dict[str, Any]]:
        """
        获取所有角色，支持状态和类型筛选
        
        Args:
            db: 数据库连接
            status: 角色状态筛选（可选值：active, inactive）
            is_system: 是否系统角色筛选（True: 只返回系统角色, False: 只返回自定义角色, None: 返回所有）
            scope: 角色作用域筛选（system, project）
        """
        # 构建查询条件
        query = {}
        if status:
            query["is_active"] = status == "active"
        if is_system is not None:
            query["is_system"] = is_system
        if scope:
            query["scope"] = scope
        
        # 查询角色，系统角色优先，然后按创建时间排序
        cursor = db.roles.find(query).sort([("is_system", -1), ("created_at", -1)])
        roles = await cursor.to_list(length=None)
        
        # 转换数据格式
        role_list = []
        for role in roles:
            role["id"] = str(role["_id"])
            del role["_id"]
            role_list.append(role)
        
        return role_list
    
    @staticmethod
    async def get_system_roles(db: AsyncIOMotorDatabase) -> List[Dict[str, Any]]:
        """
        获取所有系统角色
        系统角色是预定义的角色，权限由 ROLE_PERMISSIONS 定义
        """
        return await RoleService.get_roles(db, is_system=True)
    
    @staticmethod
    async def get_custom_roles(db: AsyncIOMotorDatabase) -> List[Dict[str, Any]]:
        """
        获取所有自定义角色
        自定义角色是用户创建的角色，权限可以自定义
        """
        return await RoleService.get_roles(db, is_system=False)
    
    @staticmethod
    async def create_role(db: AsyncIOMotorDatabase, role_data: RoleCreate) -> Dict[str, Any]:
        """
        创建新角色
        如果角色代码为空，会根据角色名称自动生成
        """
        from app.utils.role_code_generator import generate_role_code, normalize_role_code
        
        # 如果角色代码为空，自动生成
        if not role_data.code or not role_data.code.strip():
            # 获取所有已存在的角色代码
            existing_roles = await db.roles.find({}, {"code": 1}).to_list(length=None)
            existing_codes = [role.get("code") for role in existing_roles if role.get("code")]
            
            # 生成角色代码
            role_data.code = generate_role_code(role_data.name, existing_codes)
        else:
            # 规范化角色代码
            role_data.code = normalize_role_code(role_data.code)
        
        # 检查角色代码是否已存在
        existing_role = await db.roles.find_one({"code": role_data.code})
        if existing_role:
            raise ValueError("角色代码已存在")
        
        # 创建角色数据
        role_dict = role_data.model_dump()
        role_dict.update({
            "is_system": False,
            "created_at": datetime.utcnow(),
            "updated_at": datetime.utcnow()
        })
        
        # 插入数据库
        result = await db.roles.insert_one(role_dict)
        
        # 获取创建的角色
        created_role = await db.roles.find_one({"_id": result.inserted_id})
        created_role["id"] = str(created_role["_id"])
        del created_role["_id"]
        
        return created_role
    
    @staticmethod
    async def get_role(db: AsyncIOMotorDatabase, role_id: str) -> Dict[str, Any]:
        """获取角色详情"""
        # 查询角色
        role = await db.roles.find_one({"_id": ObjectId(role_id)})
        if not role:
            raise ValueError("角色不存在")
        
        role["id"] = str(role["_id"])
        del role["_id"]
        
        return role
    
    @staticmethod
    async def update_role(db: AsyncIOMotorDatabase, role_id: str, role_data: RoleUpdate) -> Dict[str, Any]:
        """
        更新角色
        系统角色不允许修改名称、代码和权限（权限由 ROLE_PERMISSIONS 自动管理）
        自定义角色可以修改所有字段
        """
        # 检查角色是否存在
        role = await db.roles.find_one({"_id": ObjectId(role_id)})
        if not role:
            raise ValueError("角色不存在")
        
        is_system = role.get("is_system", False)
        
        # 系统角色限制：不允许修改名称、代码、权限
        if is_system:
            if role_data.name is not None:
                raise ValueError("系统角色的名称不允许修改")
            if role_data.code is not None:
                raise ValueError("系统角色的代码不允许修改")
            if role_data.permissions is not None:
                raise ValueError("系统角色的权限由系统自动管理，不允许手动修改")
        
        # 如果更新角色代码，检查是否已存在
        if role_data.code and role_data.code != role["code"]:
            existing_role = await db.roles.find_one({"code": role_data.code})
            if existing_role:
                raise ValueError("角色代码已存在")
        
        # 构建更新数据
        update_data = {}
        for field, value in role_data.model_dump(exclude_unset=True).items():
            if value is not None:
                update_data[field] = value
        
        # 如果有更新数据
        if update_data:
            update_data["updated_at"] = datetime.utcnow()
            await db.roles.update_one(
                {"_id": ObjectId(role_id)},
                {"$set": update_data}
            )
        
        # 获取更新后的角色
        updated_role = await db.roles.find_one({"_id": ObjectId(role_id)})
        updated_role["id"] = str(updated_role["_id"])
        del updated_role["_id"]
        
        return updated_role
    
    @staticmethod
    async def delete_role(db: AsyncIOMotorDatabase, role_id: str) -> None:
        """
        删除角色
        删除前会检查：
        1. 角色是否存在
        2. 是否为系统角色（系统角色不允许删除）
        3. 是否有用户使用该角色（如果有用户使用，不允许删除）
        4. 是否有项目成员使用该角色（如果有项目成员使用，不允许删除）
        """
        # 验证角色ID格式
        if not ObjectId.is_valid(role_id):
            raise ValueError("无效的角色ID格式")
        
        # 检查角色是否存在
        role = await db.roles.find_one({"_id": ObjectId(role_id)})
        if not role:
            raise ValueError("角色不存在")
        
        role_code = role.get("code")
        if not role_code:
            raise ValueError("角色代码不存在")
        
        # 检查是否为系统角色
        if role.get("is_system", False):
            raise ValueError("系统角色不允许删除")
        
        # 检查是否有用户使用该角色
        # roles 字段是数组，需要使用 $in 操作符或直接匹配（MongoDB会自动处理数组）
        # 为了明确性，使用 $in 操作符
        user_count = await db.users.count_documents({
            "roles": {"$in": [role_code]},
            "status": {"$ne": "deleted"}  # 排除已删除的用户
        })
        
        if user_count > 0:
            raise ValueError(f"该角色正在被 {user_count} 个用户使用，无法删除。请先将这些用户的角色移除后再删除。")
        
        # 检查是否有项目成员使用该角色
        # 检查项目成员中的角色字段（现在是列表）
        project_count = await db.projects.count_documents({
            "members.roles": role_code
        })
        
        if project_count > 0:
            raise ValueError(f"该角色正在被 {project_count} 个项目使用，无法删除。请先将这些项目成员的角色移除后再删除。")
        
        # 所有检查通过，删除角色
        await db.roles.delete_one({"_id": ObjectId(role_id)})
    
    @staticmethod
    async def init_default_roles(db: AsyncIOMotorDatabase) -> Dict[str, int]:
        """
        初始化系统角色（覆盖初始化）
        如果角色已存在且是系统角色，会完全覆盖其定义（名称、描述、权限等）
        确保数据库中的系统角色与代码定义完全一致
        只处理系统角色，不影响自定义角色
        """
        created_count = 0
        updated_count = 0
        
        for role_data in DEFAULT_ROLES:
            role_code = role_data.get("code")
            if not role_code:
                continue
            
            # 检查角色是否已存在
            existing_role = await db.roles.find_one({"code": role_code})
            
            if not existing_role:
                # 创建新角色
                role_dict = role_data.copy()
                role_dict.update({
                    "is_system": True,
                    "is_active": True,
                    "created_at": datetime.utcnow(),
                    "updated_at": datetime.utcnow()
                })
                
                await db.roles.insert_one(role_dict)
                created_count += 1
            else:
                # 如果角色已存在，检查是否为系统角色
                if existing_role.get("is_system", False):
                    # 系统角色：完全覆盖初始化（强制覆盖所有字段）
                    # 无论字段是否相同，都执行覆盖操作，确保数据库中的定义与代码完全一致
                    # 保留创建时间，更新其他所有字段
                    await db.roles.update_one(
                        {"code": role_code, "is_system": True},
                        {
                            "$set": {
                                "name": role_data.get("name"),
                                "description": role_data.get("description"),
                                "permissions": role_data.get("permissions", []),
                                "scope": role_data.get("scope", RoleScope.PROJECT),
                                "is_system": True,
                                "is_active": True,
                                "updated_at": datetime.utcnow()
                            }
                        }
                    )
                    updated_count += 1
                else:
                    # 如果存在同名代码的自定义角色，跳过（不覆盖自定义角色）
                    # 这种情况不应该发生，因为系统角色代码是预定义的
                    # 但为了安全，我们记录日志并跳过
                    import logging
                    logger = logging.getLogger(__name__)
                    logger.warning(f"发现自定义角色使用了系统角色代码: {role_code}，跳过初始化")
        
        return {
            "created_count": created_count,
            "updated_count": updated_count,
            "total_count": len(DEFAULT_ROLES)
        }
    
    @staticmethod
    async def _init_default_roles(db: AsyncIOMotorDatabase) -> List[Dict[str, Any]]:
        """
        内部方法：初始化默认角色并返回（覆盖初始化）
        先执行覆盖初始化，然后返回所有系统角色
        """
        # 先执行覆盖初始化
        await RoleService.init_default_roles(db)
        
        # 查询所有系统角色并返回
        default_roles = []
        for role_data in DEFAULT_ROLES:
            role_code = role_data.get("code")
            if not role_code:
                continue
                
            existing_role = await db.roles.find_one({"code": role_code, "is_system": True})
            
            if existing_role:
                # 转换为返回格式
                role_dict = {
                    "id": str(existing_role["_id"]),
                    "name": existing_role.get("name"),
                    "code": existing_role.get("code"),
                    "description": existing_role.get("description", ""),
                    "permissions": existing_role.get("permissions", []),
                    "is_system": existing_role.get("is_system", True),
                    "is_active": existing_role.get("is_active", True),
                    "created_at": existing_role.get("created_at"),
                    "updated_at": existing_role.get("updated_at")
                }
                
                # 确保 scope 存在
                if "scope" not in role_dict:
                    role_dict["scope"] = existing_role.get("scope", RoleScope.PROJECT)
                
                # 移除 MongoDB 的 _id 字段（如果存在）
                if "_id" in role_dict:
                    del role_dict["_id"]
                
                default_roles.append(role_dict)
        
        return default_roles
    
    @staticmethod
    def get_roles_from_enum() -> List[Dict[str, str]]:
        """从 SystemRole 枚举获取系统角色列表"""
        from app.enums.role import SystemRole, get_system_role_name
        
        roles = []
        for role in SystemRole:
            roles.append({
                "label": get_system_role_name(role),
                "value": role.value,
                "code": role.value
            })
        return roles
    
    @staticmethod
    def get_default_role_config() -> Dict[SystemRole, Dict[str, str]]:
        """获取默认角色配置（使用 SystemRole 枚举）"""
        return get_system_role_config()
    
    @staticmethod
    def get_role_permissions(role_code: str) -> List[str]:
        """
        获取指定角色的权限列表
        
        Args:
            role_code: 角色代码
            
        Returns:
            权限列表
        """
        from app.enums.role import is_system_role
        
        try:
            # 优先检查是否为系统角色
            if is_system_role(role_code):
                # 系统角色代码映射到 UserRole（用于从 ROLE_PERMISSIONS 获取权限）
                role_mapping = {
                    "admin": UserRole.ADMIN,
                    "project_manager": UserRole.PROJECT_MANAGER,
                    "developer": UserRole.DEVELOPER,
                    "tester": UserRole.TESTER,
                    "viewer": UserRole.VIEWER,
                    "user": UserRole.USER,
                }
                user_role = role_mapping.get(role_code)
                if user_role:
                    return ROLE_PERMISSIONS.get(user_role, [])
            
            # 如果不是系统角色，尝试直接使用 UserRole
            role_enum = UserRole(role_code)
            return ROLE_PERMISSIONS.get(role_enum, [])
        except ValueError:
            # 如果角色代码不在枚举中，返回空列表
            return []
    
    @staticmethod
    async def sync_role_permissions(db: AsyncIOMotorDatabase, role_code: str) -> bool:
        """
        同步指定系统角色的权限列表到数据库
        用于更新已存在系统角色的权限（仅适用于系统角色）
        
        Args:
            db: 数据库连接
            role_code: 角色代码
            
        Returns:
            是否成功同步
        """
        try:
            # 检查角色是否存在且为系统角色
            role = await db.roles.find_one({"code": role_code})
            if not role:
                return False
            
            if not role.get("is_system", False):
                # 自定义角色的权限不需要同步
                return False
            
            # 获取角色的权限列表
            permissions = RoleService.get_role_permissions(role_code)
            if not permissions:
                return False
            
            # 更新数据库中的角色权限
            result = await db.roles.update_one(
                {"code": role_code, "is_system": True},
                {
                    "$set": {
                        "permissions": permissions,
                        "updated_at": datetime.utcnow()
                    }
                }
            )
            
            return result.modified_count > 0
        except Exception:
            return False
    
    @staticmethod
    async def sync_all_system_roles(db: AsyncIOMotorDatabase) -> Dict[str, int]:
        """
        同步所有系统角色的权限到数据库
        确保数据库中系统角色的权限与 ROLE_PERMISSIONS 一致
        使用 SystemRole 枚举遍历所有系统角色
        
        Returns:
            包含同步结果的字典
        """
        from app.enums.role import SystemRole
        
        synced_count = 0
        failed_count = 0
        
        # 使用 SystemRole 枚举遍历所有系统角色
        for system_role in SystemRole:
            role_code = system_role.value
            success = await RoleService.sync_role_permissions(db, role_code)
            if success:
                synced_count += 1
            else:
                failed_count += 1
        
        return {
            "synced_count": synced_count,
            "failed_count": failed_count,
            "total_count": len(SystemRole)
        }
    
    @staticmethod
    async def get_role_with_permissions(db: AsyncIOMotorDatabase, role_id: str) -> Dict[str, Any]:
        """
        获取角色详情，包括权限信息
        如果是系统角色，会从 ROLE_PERMISSIONS 获取最新的权限定义
        """
        role = await RoleService.get_role(db, role_id)
        
        # 如果是系统角色，确保权限是最新的
        if role.get("is_system", False):
            role_code = role.get("code")
            if role_code:
                # 从 ROLE_PERMISSIONS 获取最新的权限定义
                latest_permissions = RoleService.get_role_permissions(role_code)
                if latest_permissions:
                    role["permissions"] = latest_permissions
        
        return role
    
    @staticmethod
    async def get_role_codes_by_ids(db: AsyncIOMotorDatabase, role_ids: List[str]) -> List[str]:
        """通过角色ID列表获取角色代码列表"""
        if not role_ids:
            return []
        
        object_ids = []
        for rid in role_ids:
            if ObjectId.is_valid(rid):
                object_ids.append(ObjectId(rid))
        
        if not object_ids:
            return []
            
        cursor = db.roles.find({"_id": {"$in": object_ids}}, {"code": 1})
        roles = await cursor.to_list(length=None)
        return [role["code"] for role in roles if "code" in role]